(function(global){
	var doc = document,
	timeout = window.setTimeout,
	toString = Object.prototype.toString,
	head = doc.head || doc.getElementsByTagName('head'),
	isGecko = ('MozAppearance' in doc.documentElement.style),
	isOpera = window.opera && toString.call( window.opera ) == '[object Opera]',
	isWebkit = ('webkitAppearance' in doc.documentElement.style),
	page = /^[^?#]*\//.exec(location.href)[0],
	domain = /^\w+\:\/\/\/?[^\/]+/.exec(page)[0];

	var configs = {
		base: '',
		duplicates: false,
		timeout: 20000, // 20s
		debug: false
	};
	var types = {js: 'js', css: 'css'};
	var modules = [];
	
	var prefixes = {
		js: function(res, value) {
			res.type = types.js;
			return res;
		},
		css: function(res, value) {
		
		},
		preload: function(res, value) {
		
		},
		module: function(res, value) {
			for (var i = 0; i < modules.length; i++) {
				if (res.url == modules[i].name) {
					res.type = modules[i].type;
					res.url = modules[i].url;
					break;
				}
			}
			return res;
		}
	};
	
	var util = {
		isString: function(val) {
			return toString.call(val) === '[object String]';
		},
		isFunction: function(val) {
			return toString.call(val) === '[object Function]';
		},
		isObject: function(val) {
			return toString.call(val) === '[object Object]';
		},
		isArray: Array.isArray || function(val) {
			return toString.call(val) === '[object Array]';
		},
		merge: function(source, target) {
			for (var k in source) { 
				if (source.hasOwnProperty(k)) {
					target[k] = source[k];
				}
			}
			return target;
		},
		getSupportResType: function(url) {
			var index = url.lastIndexOf('.'), type = '';	
			if (index > 0) {
				type = url.substring(index+1).toLowerCase();
				if (!types.hasOwnProperty(type)) {
					type = '';	
				}
			}
			return type;
		},
		getFileName: function(url) {
			var index = url.lastIndexOf('/');
			if (index >= 0) {
				index++;
			} else {
				index = 0;
			}
			return  url.substring(index, url.indexOf('.', index));
		},
		getFullUrl: function(url) {
			var regex = /^\w+\:\/\//;
			if (url == '') return url;
			if (!regex.test(url)) {
				if (url.charAt(0) == '/') {
					url = domain + url;
				} else {
					url = ((configs.base == '') ? page : configs.base) + url;
				}
			}
			return url;
		},
		log: function(msg) {
			if (global.console && configs.debug) {
				global.console.log(msg);
			}
		}
	};
	
	function wrapResource(origUrl) {
		var parts, i;
		var resource = {
			url:'', type:'', charset:'', 
			finished: false, finishedCallback: null
		};
		
		if (util.isString(origUrl)) {
			parts   = origUrl.split('!');
			resource.url = parts.pop();
			for (i = 0; i < parts.length; i++) {
				if (prefixes.hasOwnProperty(parts[i]) && util.isFunction(prefixes[parts[i]])) {
					resource = prefixes[parts[i]](resource);
				}
			}
			
			if (resource.type == '') {
				resource.type = util.getSupportResType(resource.url);
			}
		}
		
		return resource;
	}
	
	function addScriptLoadEventListener(elem, regItem, evt, callback) {
		elem.onload = elem.onreadystatechange = function() {
			if ((elem.readyState && elem.readyState !== 'complete' && elem.readyState !== 'loaded') || regItem[evt]) {
				return;
			}
			
			elem.onload = elem.onreadystatechange = null;
			closeTimers(regItem);
			callback();
		};
	}
	
	function closeTimers(regItem) {
		if (regItem.timer.loadCheck) clearInterval(regItem.timer.loadCheck);
		if (regItem.timer.loadTimeout) clearTimeout(regItem.timer.loadTimeout);
		regItem.timer.loadCheck = regItem.timer.loadTimeout = null;
	}
	
	function resourceLoadFinishedCallback(registryItem) {
		registryItem.finished = true;
		for (var i=0; i<registryItem.finishedListeners.length; i++) {
			registryItem.finishedListeners[i]();
		}
		registryItem.finishedListeners = [];
	}
	
		
	function createLoader() {
		var api,chain,
		isRunBlock = false,
		registrys = {};
		
		function setCallbackRunBlock() {
			isRunBlock = true;
		}
		
		function continueCallbackRun() {
			isRunBlock = false;
		}
		
		function loadJs(resource, regItem, callback) {
			var node = doc.createElement('script');
			node.setAttribute('type', 'text/javascript');
			if (resource.charset) node.setAttribute('charset', resource.charset);
			if(node.async === true) node.async = false;
			
			addScriptLoadEventListener(node, regItem, 'finished', callback);
			
			node.setAttribute('src', resource.url);
			head.insertBefore(node, head.firstChild);
			
			regItem.timer.loadTimeout = setTimeout(function() {
				closeTimers(regItem);
				callback();
			}, configs.timeout);
		}
		
		function loadCss(resource, regItem, callback) {			
			var node = doc.createElement('link');
			node.setAttribute('rel', 'stylesheet');
			if (resource.charset) node.setAttribute('charset', resource.charset);
			
			node.setAttribute('href', resource.url);
			head.appendChild(node);
			
			if (node.attachEvent) {
				node.attachEvent('onload', callback);
			} else {
				regItem.timer.loadCheck = setInterval(function() {
					if (isWebkit) {
						if (node['sheet']) {
							closeTimers(regItem);
							callback();
						}
					} else if (node['sheet']) {
						try {
							if (node['sheet'].cssRules) {
								closeTimers(regItem);
								callback();
							}
						} catch (ex) {
							if ((ex.code === 1000) || ( ex.message == 'security' || ex.message == 'denied' )) {
								closeTimers(regItem);
								callback();
							}
						}
					}
				}, 20);
			}
			
			regItem.timer.loadTimeout = setTimeout(function() {
				closeTimers(regItem);
				callback();
			}, configs.timeout);
		}
		
		function loadResource(resource, registryItem, callback) {
			timeout(function(){
				if ('item' in head) {
					if (!head[0]) {
						timeout(arguments.callee, 25);
						return;
					}
					head = head[0]; 
				}
				
				if (resource.type == types.js) {
					loadJs(resource, registryItem, callback);
				} else if(resource.type == types.css) {
					loadCss(resource, registryItem, callback);
				}
			}, 0);
		}
		
		function registryResource(resource, chainGroup) {
			var registryItem,
				registryItems,
				finishedCallback = function(){ resource.finishedCallback(resource, chainGroup); }
			;
						
			if (!registrys[resource.url]) registrys[resource.url] = {items:[], finished:false};
			registryItems = registrys[resource.url].items;

			if (configs.duplicates || registryItems.length == 0) {
				registryItem = registryItems[registryItems.length] = {
					timer: {loadCheck: null, loadTimeout: null},
					finished: false,
					finishedListeners: [finishedCallback]
				};

				loadResource(resource, registryItem, function(){ 
					resourceLoadFinishedCallback(registryItem); 
				});
			} else {
				registryItem = registryItems[0];
				if (registryItem.finished) {
					finishedCallback();
				} else {
					registryItem.finishedListeners.push(finishedCallback);
				}
			}
		}
				
		function createChain() {
			var chainAPI,
			chain = [],
			group = false,
			cursor = 0;
		
			function initChainGroup() {
				if (!group || !group.resources) {
					chain.push(group = {resources:[], finished:true});
				}
			}
			
			function addResourceToCurrentlyGroup(resource) {
				group.finished = false;
				group.resources.push(resource);
			}
			
			function chainResourceLoadFinishedCallback(resource, chainGroup) {
				resource.finished = true;
				
				for (var i = 0; i < chainGroup.resources.length; i++) {
					if (!chainGroup.resources[i].finished) return;
				}
				
				chainGroup.finished = true;
				executeRunnableEventCallbacks();
			}
						
			function addLoadFinishedCallbackEvents(){
				if (arguments.length > 0) {
					for (var i=0; i<arguments.length; i++) {
						chain.push(arguments[i]);
					}
				} 
				
				group = false;
				executeRunnableEventCallbacks();
			}
									
			function executeRunnableEventCallbacks() {
				if (isRunBlock) return;
				
				while (cursor < chain.length) {
					if (util.isFunction(chain[cursor])) {
						//util.log("executing: "+ chain[cursor]);
						try { chain[cursor++](); } catch (err) {/*util.log("error caught: ", err);*/}
						continue;
					} else if (!chain[cursor].finished) {
						break;
					}
					cursor++;
				}
				
				if (cursor == chain.length) {
					group = false;
				}
			}
			
			function addSingleResource(res, callback) {
				var resource;
				if (util.isObject(res)) {
						resource = wrapResource(res.url);
						delete res.url;
						resource = util.merge(res, resource);
				} else {
						resource = wrapResource(res);
				}
				resource.url = util.getFullUrl(resource.url);
				
				if (resource.url != '' && resource.type != '') {
					initChainGroup();
					
					resource.finishedCallback = function(res, chainGroup){
						//util.log(res.url +" is load finished.");
						chainResourceLoadFinishedCallback(res, chainGroup);
					};
					
					addResourceToCurrentlyGroup(resource);
					registryResource(resource, group);
					
					if (null != callback) {
						addResourcesCallbackEvents(resource, callback);
					}
				}
			}
			
			function addResourcesCallbackEvents(resource, callback) {
				if (resource.url == '' || resource.type == '') return;
				
				if (callback && util.isFunction(callback)) {
					addLoadFinishedCallbackEvents(callback);
				} else {
					addLoadFinishedCallbackEvents();
				}
			}
			
			chainAPI = {
				load: function(resources, callback) {
					var i,resource;
					
					if (util.isArray(resources)) {
						for (i = 0; i < resources.length; i++) {
							resource = wrapResource(resources[i]);
							resource.url = util.getFullUrl(resource.url);
							
							addSingleResource(resource, null);
						}
						addResourcesCallbackEvents(resource, callback);
					} else {
						addSingleResource(resources, callback);
					}
					
					return chainAPI;
				},
				exec: function() {
					executeRunnableEventCallbacks();
				},
				after: function(resources, callback) {
					return chainAPI;
				}
			};
			
			return {
				load: chainAPI.load,
				exec: chainAPI.exec,
				after: chainAPI.after
			};
		}
		
		api = {
			types: types,
			config: function(options) {
				util.merge(options, configs);
				return api;
			},
			modules: function(mods) {
				if (util.isObject(mods)) {
					if (mods.name && mods.type && mods.url)  {
						modules.push(mods);
					}
				} else if (util.isArray(mods)) {
					for (var i = 0; i < mods.length; i++) {
						api.modules(mods[i]);
					}
				}
				return api;
			},
			block: function() {
				setCallbackRunBlock();
				return api;
			},
			run: function() {
				continueCallbackRun();
				chain.exec();
				return api;
			},
			load: function() {
				chain = createChain();
				return chain.load.apply(null, arguments);
			}
		};
		
		return api;
	}
	
	// ----------------------------------------------------------------------------------------------
	global.loader = createLoader();
	global.loader.noConflict = function() {
		return createLoader();
	};
	
	// required: shim for FF <= 3.5 not having document.readyState
	if (doc.readyState == null && doc.addEventListener) {
		doc.readyState = 'loading';
		doc.addEventListener('DOMContentLoaded', handler = function(){
			doc.removeEventListener('DOMContentLoaded', handler, 0);
			doc.readyState = 'complete';
		}, 0);
	}
})(this);

/* global */
var g_globals = {objects:{}};
function debug(msg) {if (this.console) {console.log(msg);}}
var Class={create:function(){return function(){this.initialize.apply(this,arguments)}},extend:function(c,b){for(var a in b){c[a]=b[a]}return c}};
try{document.execCommand('BackgroundImageCache', false, true );}catch(e){}

/* global functions */
function noLink(){return;}
function getJqueryElement(el) {
	if (!el) return null;
	var element = null;
	if ('string' === $.type(el)) element = $('#'+ el);
	else element = el;
	if (0 == element.size()) element = null;
	return element;
}
/* init loader */
loader.config({base:'http://static.test.com/js/'}).modules([
	{name: 'jquery', type: loader.types.js, url: 'jquery.js?v=1.6.2'},
	{name: 'common', type: loader.types.js, url: 'web.common.js'},
	{name: 'auto-drop', type: loader.types.js, url: 'web.auto-drop.js'},
	{name: 'form-ui', type: loader.types.js, url: 'web.form-ui.js'},
	{name: 'calendar', type: loader.types.js, url: 'web.calendar.js'},
	{name: 'popup', type: loader.types.js, url: 'web.popup.js'},
	{name: 'rating', type: loader.types.js, url: 'web.rating.js'},
	{name: 'drag', type: loader.types.js, url: 'web.drag.js'},
	{name: 'scroll-loading', type: loader.types.js, url: 'web.scroll-loading.js'},
	{name: 'buyer-scroll', type: loader.types.js, url: 'web.buyer-scroll.js'},
	{name: 'action-select', type: loader.types.js, url: 'web.action-select.js'},
	{name: 'goods-gallery', type: loader.types.js, url: 'web.goods-gallery.js'},
	{name: 'item-timer', type: loader.types.js, url: 'web.item-timer.js'},
	{name: 'shipping-cart', type: loader.types.js, url: 'web.shipping-cart.js'},
	{name: 'cycle', type: loader.types.js, url: 'jquery.cycle.js'},
	{name: 'swfobject', type: loader.types.js, url: 'jquery.swfobject.js?v=1.1.1'}
]);

